<?php

namespace App\Services;

use App\Models\MarketActivityQrCodeModel;
use App\Util\FileUtil;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use QRcode;

class MarketActivityQrCodeService extends BaseService
{
    /**
     * @var static 单例模式
     */
    protected static $instance;

    /**
     * 生成 token
     * @author Chris Yu <chrisyu@crabapple.top> 2023/11/21
     */
    public function generateToken($diyInfo)
    {
        // 添加一些额外的随机性，例如使用随机字符串作为盐
        $salt = uniqid();

        // 将数据与盐结合
        $key = $diyInfo['id'];
        $combinedData = $key . $salt;

        // 使用SHA-256算法生成散列值
        $hashedValue = hash('sha1', $combinedData);

        // 最终的令牌可以包含数据、盐和散列值，以便验证时可以再次计算散列值
        $token = $salt . ':' . $hashedValue;

        return [
            'token' => base64_encode($token),
            'salt' => $salt,
        ];
    }

    /**
     * 验证 token
     * @author Chris Yu <chrisyu@crabapple.top> 2023/11/21
     */
    public function validateToken($token, &$errorMsg = '')
    {
        if (empty($token)) {
            $errorMsg = '缺少 token';
            Log::notice($errorMsg, ['location' => __METHOD__ . '[' . __LINE__ . ']']);
            return false;
        }

        $tokenBase64Decode = base64_decode($token);
        if ($tokenBase64Decode === false) {
            $errorMsg = 'token 解析失败';
            Log::notice($errorMsg, ['token' => $token, 'location' => __METHOD__ . '[' . __LINE__ . ']']);
            return false;
        }
        $tokenArray = explode(':', $tokenBase64Decode);
        if (count($tokenArray) < 2) {
            $errorMsg = 'token 解析失败';
            Log::notice($errorMsg, ['token' => $token, 'location' => __METHOD__ . '[' . __LINE__ . ']']);
            return false;
        }
        list($salt, $hashedValue) = $tokenArray;

        $diyQrCode = MarketActivityQrCodeModel::where('token_salt', $salt)->first();
        if (empty($diyQrCode)) {
            $errorMsg = '二维码无效';
            Log::notice($errorMsg, ['token' => $token, 'location' => __METHOD__ . '[' . __LINE__ . ']']);
            return false;
        }

        // 使用传递过来的数据和盐结合
        $key = $diyQrCode['diy_id'];
        $combinedData = $key . $salt;

        // 使用相同的算法计算散列值
        $calculatedHash = hash('sha1', $combinedData);

        // 检查计算出的散列值是否与 token 中的散列值一致
        if ($calculatedHash !== $hashedValue) {
            $errorMsg = 'token 校验错误';
            Log::notice($errorMsg, ['token' => $token, 'location' => __METHOD__ . '[' . __LINE__ . ']']);
            return false;
        }
        return true;
    }

    /**
     * 生成二维码(为机构)
     * @author Chris Yu <chrisyu@crabapple.top> 2023/11/22
     */
    public function generateForOrganization($diy, $organizationId)
    {
        // 有且未过期的二维码，直接返回
        $diyQrCode = MarketActivityQrCodeModel::query()
            ->where('type', MarketActivityQrCodeModel::TYPE_ORGANIZATION)
            ->where('diy_id', $diy['id'])
            ->where('organization_id', $organizationId)
            ->where('status', MarketActivityQrCodeModel::STATUS_ENABLE)
            ->where('generate_cache_time', '>', date('Y-m-d H:i:s', time()))
            ->orderBy('id', 'desc')
            // ->lockForUpdate()
            ->first();

        if (!empty($diyQrCode)) {
            return $diyQrCode;
        }

        // 二维码内容
        $pageUrlInfo = config('common.activity_page.page_url');
        $pageUrlInfo[2] = sprintf($pageUrlInfo[2], $diy['id'], $organizationId);
        $content = $pageUrlInfo[0] . urlencode($pageUrlInfo[1] . urlencode($pageUrlInfo[2]));
        // 创建二维码
        $createResult = $this->generateExec($diy, $content);
        if (!$createResult) {
            return false;
        }

        $saveData = [
            'type' => MarketActivityQrCodeModel::TYPE_ORGANIZATION,
            'diy_id' => $diy['id'],
            'organization_id' => $organizationId,
            'content' => $createResult['content'],
            'image_url' => $createResult['full_url'],
            'token_salt' => $createResult['token']['salt'],
            'generate_cache_time' => date('Y-m-d H:i:s', time() + 3600 * 24 * 7),
            'creator' => AuthService::getInstance()->getAdminId(),
        ];

        $result = MarketActivityQrCodeModel::create($saveData);
        if (!$result) {
            Log::error("二维码保存失败", ['location' => __METHOD__ . '[' . __LINE__ . ']']);
            return false;
        }

        return $result;
    }

    /**
     * 生成二维码（上传到云存储）
     * @author Chris Yu <chrisyu@crabapple.top> 2023/11/21
     */
    public function generateExec($diy, $content)
    {
        // todo 同前端协商，token 暂不需要用了 =.=
        $tokenArray = MarketActivityQrCodeService::getInstance()->generateToken($diy);

        // 生成二维码图片
        require_once base_path() . '/app/Libs/Vendor/phpqrcode/qrlib.php';

        $saveDir = storage_path() . '/temp/qrcode/';
        $fileName = uniqid().'.png';
        $filePath = $saveDir . $fileName; // 二维码图片保存地址
        FileUtil::createFolders($saveDir);

        QRcode::png($content, $filePath, QR_ECLEVEL_L, 6, 3);
        $logoFilePath = base_path() . '/public/img/alipay_icon.png'; // logo 图片地址
        // 添加 logo
        $image = imagecreatefromstring(file_get_contents($filePath));
        $logo = imagecreatefromstring(file_get_contents($logoFilePath));
        $QR_width = imagesx($image); // 二维码图片宽度
        $QR_height = imagesy($image); // 二维码图片高度
        $logo_width = imagesx($logo); // logo图片宽度
        $logo_height = imagesy($logo); // logo图片高度
        $logo_qr_width = $QR_width / 5;
        $scale = $logo_width / $logo_qr_width;
        $logo_qr_height = $logo_height / $scale;
        $from_width = ($QR_width - $logo_qr_width) / 2;
        // 重新组合图片并调整大小
        imagecopyresampled($image, $logo, $from_width, $from_width, 0, 0, $logo_qr_width,
            $logo_qr_height, $logo_width, $logo_height);
        // 输出图片
        imagepng($image, $filePath);

        // 上传到 OSS
        $uploadResult = FileService::getInstance()->uploadFromLocal($filePath, 'qrcode');
        if (!$uploadResult) {
            return false;
        }
        $uploadResult['content'] = $content;
        $uploadResult['token'] = $tokenArray;
        return $uploadResult;
    }
}
